<template>
    <div class="el-tree-select" :class="selectClass">
        <!-- 下拉文本 -->
        <el-select
            :style="styles"
            class="el-tree-select-input"
            v-model="labels"
            :disabled="disabled"
            popper-class="select-option"
            ref="select"
            v-bind="selectParams"
            :popper-append-to-body="false"
            :filterable="false"
            :multiple="selectParams.multiple"
            v-popover:popover
            @remove-tag="_selectRemoveTag"
            :title="labels"
            @clear="_selectClearFun"
            @focus="_popoverShowFun"
        ></el-select>
        <!-- 弹出框 -->
        <el-popover
            ref="popover"
            :placement="placement"
            :popper-class="popperClass"
            :width="width"
            v-model="visible"
            trigger="click"
        >
            <!-- 是否显示搜索框 -->
            <el-input
                v-if="treeParams.filterable"
                v-model="keywords"
                size="mini"
                class="input-with-select mb10"
                @change="_searchFun"
            >
                <el-button slot="append" icon="el-icon-search"></el-button>
            </el-input>
            <el-scrollbar
                tag="div"
                wrap-class="el-select-dropdown__wrap"
                view-class="el-select-dropdown__list"
                class="is-empty"
            >
                <!-- 树列表 :indent="0" 为必须配置，避免样式错乱-->
                <uxTree
                    ref="tree"
                    v-show="data.length > 0"
                    v-bind="treeParams"
                    :data="data"
                    :node-key="propsValue"
                    :draggable="false"
                    :current-node-key="ids.length > 0 ? ids[0] : ''"
                    :show-checkbox="selectParams.multiple"
                    :filter-node-method="_filterFun"
                    :render-content="treeRenderFun"
                    @node-click="_treeNodeClickFun"
                    @check="_treeCheckFun"
                    :indent="0"
                ></uxTree>
                <!-- 暂无数据 -->
                <div v-if="data.length === 0" class="no-data">暂无数据</div>
            </el-scrollbar>
        </el-popover>
    </div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from "vue-property-decorator";
import { on } from "@/components/util/dom";
import util from "@/components/util/index";
import { construct } from "@aximario/json-tree";
import uxTree from "@/components/tree";
@Component({
    name: "uxInputTree",
    components: { uxTree }
})
export default class uxInputTree extends Vue {
    // 当前选中值
    // 注意如果值类型为Array,multiple会被强制设置为true
    @Prop({ default: "" }) readonly value: string | any | number;
    // el-select样式
    @Prop({
        default: function () {
            return {};
        }
    })
    readonly styles!: any;
    // 下拉框 挂类
    @Prop({ default: "" }) readonly selectClass!: string;
    // popover 挂类
    @Prop({ default: "" }) readonly popoverClass!: string;
    // 是否禁用文本框
    @Prop({ default: false }) readonly disabled!: boolean;
    // 查询时是否本地查询
    @Prop({ default: true }) readonly isLocalQuery!: boolean;
    // 弹出框位置
    @Prop({ default: "bottom" }) readonly placement!: string;
    // 树渲染方法，具体参考el-tree Function(h, { node, data, store }) {}
    @Prop(Function) readonly treeRenderFun!: Function;
    // 非树形结构数据
    @Prop() readonly anyData!: Array<any>;
    // 用于动态绑定数据
    @Prop() treeData!: any;
    /*
        文本框参数，几乎支持el-select所有的API<br>
        取消参数：<br>
        设定下拉框的弹出框隐藏：<br>
        `:popper-append-to-body="false"` <br>
        搜索从弹出框里面执行： <br>
        `:filterable="false"`
     */
    @Prop({
        /*
            Object默认参数：<br><br>
            是否可以清空选项：<br>
            `clearable: true,`<br><br>
            是否禁用：<br>
            `disabled: false,`<br><br>
            搜索框placeholder文字：<br>
            `placeholder: '请选择',`<br><br>
        */
        default: {
            clearable: true,
            disabled: false,
            placeholder: "请选择"
        }
    })
    readonly selectParams: any;
    /*
        下拉树参数，几乎支持el-tree所有的API<br>
         取消参数:<br>
        `:show-checkbox="selectParams.multiple"`<br>
        使用下拉框参数multiple判断是否对树进行多选<br>
        取消对el-tree的人为传参show-checkbox<br>
        `:node-key="propsValue"`     自动获取treeParams.props.value<br>
        `:draggable="false"`         屏蔽拖动
        */
    @Prop({
        /*
            Object默认参数：<br><br>
            在有子级的情况下是否点击父级关闭弹出框,false 只能点击子级关闭弹出框：`clickParent: false`<br><br>
            是否显示搜索框：<br>
            `filterable: false`<br><br>
            下拉树的数据：<br>
            `data:[]`<br><br>
            下拉树的props：<br>
            `props: {`<br>
                `children: 'children',`<br>
                `label: 'name',`<br>
                `value: 'flowId',`<br>
                `disabled: 'disabled'`<br>
            `}`
        */
        default: function () {
            return {
                clickParent: false,
                filterable: false,
                data: [],
                props: {
                    children: "children",
                    label: "label",
                    value: "id",
                    disabled: "disabled"
                }
            };
        }
    })
    readonly treeParams: any;
    // 默认node-key配置
    propsValue: string = "id";
    // 默认selectParams.props.label配置
    propsLabel: string = "label";
    // 默认selectParams.props.code配置,可能有空的情况
    propsCode!: string;
    // 默认selectParams.props.disabled配置
    propsDisabled: string = "disabled";
    // 默认selectParams.props.children配置
    propsChildren: string = "children";
    // 数据源
    data: any = [];
    // 搜索关键词
    keywords: string = "";
    // 存储名称，用于下拉框显示内容
    labels: string | any = "";
    // 当前设置的值
    ids: any = [];
    // 选中节点
    selectNodes: any = [];
    // popover v-model
    visible: boolean = false;
    width: Number = 150;

    // 监听
    @Watch("anyData")
    updateAnyData() {
        this.setAnyData();
    }
    // 设置扁平数据
    setAnyData() {
        const me = this as any,
            data = me.anyData,
            props = me.treeParams.props;
        if (!me.data.length && data) {
            const treeData = construct(data, {
                id: props.id,
                pid: props.layer,
                children: props.children
            });
            this.treeDataUpdateFun(treeData);
        }
    }

    // 监听treeData
    @Watch("treeData")
    updateTreeData(data: any) {
        // 更新数据
        this.treeDataUpdateFun(data);
    }

    // 监听ids
    @Watch("ids")
    // 当isExpandAll变化时d
    updateIds(val: any) {
        if (val !== undefined) {
            this._setSelectNodeFun(val);
        }
    }

    // 监听value
    @Watch("value")
    // 当isExpandAll变化时
    updateValue(val: any) {
        if (this.ids !== val) {
            this._setMultipleFun();
            if (this.selectParams.multiple) {
                this.ids = [...val];
            } else {
                this.ids = val === "" ? [] : [val];
            }
        }
    }

    get popperClass(): string {
        const _c = "el-tree-select-popper " + this.popoverClass;
        return this.disabled ? _c + " disabled " : _c;
    }
    // 初始化
    created() {
        const me = this,
            { props, data } = me.treeParams;
        me._setMultipleFun();
        me.propsValue = props.value;
        me.propsLabel = props.label;
        me.propsCode = props.code || null; // 可能为空
        me.propsDisabled = props.disabled;
        me.propsChildren = props.children;
        // 如果data有值
        if (data) {
            me.data = data.length > 0 ? [...data] : [];
        }
        // 如果treeData有值，设置data
        if (me.treeData) {
            me.data = me.treeData;
        }
        // 处理扁平数据
        me.setAnyData();
        if (me.selectParams.multiple) {
            me.labels = [];
            me.ids = me.value;
        } else {
            me.labels = "";
            me.ids = me.value instanceof Array ? me.value : [this.value];
        }
    }
    // 页面初始化完成
    mounted() {
        this._updateH();
        this.$nextTick(() => {
            on(document, "mouseup", this._popoverHideFun);
        });
    }
    // 根据类型判断单选，多选
    _setMultipleFun() {
        let multiple = false;
        if (this.value instanceof Array) {
            multiple = true;
        }
        this.$set(this.selectParams, "multiple", multiple);
    }
    // 输入文本框输入内容抛出
    _searchFun() {
        /*
            对外抛出搜索方法，自行判断是走后台查询，还是前端过滤<br>
            前端过滤：this.$refs.treeSelect.$refs.tree.filter(value);<br>
            后台查询：this.$refs.treeSelect.treeDataUpdateFun(data);
            */
        const me = this as any;
        if (me.isLocalQuery) {
            me.$refs.tree.filter(me.keywords);
        } else {
            me.$emit("searchFun", me.keywords);
        }
    }
    //  根据id筛选当前树名称，以及选中树列表
    _setSelectNodeFun(ids: any) {
        // 这样写避免调用后续方法语法报错
        const el = this.$refs.tree as any;
        if (!el) {
            throw new Error("找不到tree dom");
        }
        const { multiple } = this.selectParams;
        // 长度为0，清空选择
        if (ids.length === 0 || this.data.length === 0) {
            this.labels = multiple ? [] : "";
            if (multiple) {
                el.setCheckedKeys([]);
            } else {
                el.setCurrentKey(null);
            }
            return;
        }
        if (multiple) {
            el.setCheckedKeys(ids);
            if (this.propsCode) {
                // 如果有code   labels=code(name)
                this.labels =
                    el
                        .getCheckedNodes()
                        .map((item: any) =>
                            item[this.propsCode]
                                ? item[this.propsLabel] +
                                  "(" +
                                  item[this.propsCode] +
                                  ")"
                                : item[this.propsLabel]
                        ) || [];
            } else {
                this.labels =
                    el
                        .getCheckedNodes()
                        .map((item: any) => item[this.propsLabel]) || [];
            }
        } else {
            el.setCurrentKey(ids[0]);
            if (el.getCurrentNode()) {
                if (this.propsCode) {
                    // 如果有code   labels=code(name)
                    this.labels = el.getCurrentNode()[this.propsCode]
                        ? el.getCurrentNode()[this.propsLabel] +
                          "(" +
                          el.getCurrentNode()[this.propsCode] +
                          ")"
                        : el.getCurrentNode()[this.propsLabel];
                } else {
                    this.labels = el.getCurrentNode()[this.propsLabel];
                }
            } else {
                this.labels = "";
            }
        }
        this._updatePopoverLocationFun();
    }
    // 更新popover位置
    _updatePopoverLocationFun() {
        // dom高度还没有更新，做一个延迟
        setTimeout(() => {
            const popover = this.$refs.popover as any;
            if (popover && popover.updatePopper) {
                popover && popover.updatePopper();
            }
        }, 50);
    }
    // 树过滤
    _filterFun(value: any, data: any) {
        if (!value) return true;
        return data[this.propsLabel].indexOf(value) !== -1;
    }
    // 树点击
    _treeNodeClickFun(data: any, node: any, vm: any) {
        const { multiple } = this.selectParams;
        const { clickParent } = this.treeParams;
        const { propsValue, propsChildren, propsDisabled } = this;
        if (data[propsDisabled]) {
            // 禁用
            return;
        }
        if (node.checked) {
            const value = data[this.propsValue];
            this.ids = this.ids.filter((id: any) => id !== value);
        } else {
            if (!multiple) {
                // 多选，不关闭，单选，判断是否允许点击父级关闭弹出框
                if (!clickParent) {
                    const children = data[propsChildren];
                    // 如果不允许点击父级,自身为末级，允许点击之后关闭
                    if (!children || children.length === 0) {
                        this.ids = [data[propsValue]];
                        this.visible = false;
                    } else {
                        // 不允许父级，阻止继续派发
                        return false;
                    }
                } else {
                    this.ids = [data[propsValue]];
                    this.visible = false;
                }
            } else {
                this.ids.push(data[propsValue]);
            }
        }
        this._emitFun();
        /*
            点击节点，对外抛出   `data, node, vm`<br>
            `data:` 当前点击的节点数据<br>
            `node:` 当前点击的node<br>
            `vm:` 当前组件的vm
            */
        this.$emit("node-click", data, node, vm);
    }
    // 树勾选
    _treeCheckFun(data: any, node: any, vm: any) {
        this.ids = [];
        const { propsValue } = this;
        node.checkedNodes.forEach((item: any) => {
            this.ids.push(item[propsValue]);
        });
        /*
            点击复选框，对外抛出   `data, node, vm`<br>
            `data:` 当前点击的节点数据<br>
            `node:` 当前点击的node<br>
            `vm:` 当前组件的vm
            */
        this.$emit("check", data, node, vm);
        this._emitFun();
    }
    // 下拉框移除tag时触发
    _selectRemoveTag(tag: any) {
        const { data, propsValue, propsLabel, propsChildren } = this;
        util.eachTree(
            data,
            (item: any) => {
                if (item[propsLabel] === tag) {
                    const value = item[propsValue];
                    this.ids = this.ids.filter((id: any) => id !== value);
                }
            },
            propsChildren
        );
        (this.$refs.tree as any).setCheckedKeys(this.ids);
        this.$emit("removeTag", this.ids, tag);
        this._emitFun();
    }
    // 下拉框清空数据
    _selectClearFun() {
        this.ids = [];
        const { multiple } = this.selectParams;
        // 下拉框清空，对外抛出``this.$emit('input', multiple ? [] : '');`
        this.$emit("input", multiple ? [] : "");
        // 下拉框清空，对外抛出``this.$emit('select-clear');`
        this.$emit("select-clear");
        this._updatePopoverLocationFun();
    }
    // 判断类型，抛出当前选中id
    _emitFun() {
        const { multiple } = this.selectParams;
        this.$emit(
            "input",
            multiple ? this.ids : this.ids.length > 0 ? this.ids[0] : ""
        );
        this._updatePopoverLocationFun();
    }
    // 更新宽度
    _updateH() {
        const me = this as any;
        me.$nextTick(() => {
            me.width = me.$refs.select.$el.getBoundingClientRect().width;
        });
    }
    // 显示弹出框的时候容错，查看是否和el宽度一致
    _popoverShowFun() {
        this._updateH();
    }
    // 判断是否隐藏弹出框
    _popoverHideFun(e: any) {
        const isInter = e.path.some((list: any) => {
            return (
                list.className &&
                list.className.indexOf("el-tree-select") !== -1
            );
        });
        if (!isInter) {
            this.visible = false;
        }
    }
    /**
     * @vuese
     * 树列表更新数据
     * @arg Array
     */
    treeDataUpdateFun(data: any) {
        this.data = data;
        // 数据更新完成之后，判断是否回显内容
        if (data.length > 0) {
            setTimeout(() => {
                this._setSelectNodeFun(this.ids);
            }, 300);
        }
    }

    /**
     * @vuese
     * 本地过滤方法
     * @arg String
     */
    filterFun(val: any) {
        (this.$refs.tree as any).filter(val);
    }
}
</script>
<style lang="scss">
.el-tree-select .select-option {
    display: none !important;
}

[aria-disabled="true"] > .el-tree-node__content {
    color: inherit !important;
    background: transparent !important;
    cursor: no-drop !important;
}

.el-tree-select-popper {
    max-height: 400px;
    overflow: auto;
    &.disabled {
        display: none !important;
    }
    .el-button--small {
        width: 25px !important;
        min-width: 25px !important;
    }
    &[x-placement^="bottom"] {
        margin-top: 5px;
    }
}

.mb10 {
    margin-bottom: 10px;
}

.no-data {
    height: 32px;
    line-height: 32px;
    font-size: 14px;
    color: #cccccc;
    text-align: center;
}
</style>